package main

//tset
import (
	"fmt"
	"reflect"
)

type BlockMessage struct {
}

// 拆分机构后的数据对象信息
type Object struct {
	Oid     []byte // 对象oid
	From    int8   // 对象所属机构ID
	BlockId uint64 // 对象所属区块ID
}

func (t *Object) IsEmpty() bool {
	return reflect.DeepEqual(t, Object{})
}

// 确定数据布局使用到的队列
type Line struct {
	InstituteLine [4][]Object // 机构对象队列
	instituteNum  [4]int      // 长度最长的队列ID序列
	total         int         // 剩余机构数量
	lotalTransNum int         // 总对象数量
	//lastOperation [2]int           //上次出队位置
}

// 纠删码一个条带的信息
type Quintet struct {
	BlockId uint64   // blockID
	Number  int      // 该纠删码条带在区块内所有条带中的序号
	Objs    []Object // 纠删码条带数据块对象信息
	Compute int8     // 负责计算纠删码校验块的机构ID
	Storage []int8   // 负责存储纠删码数据块的机构ID
}

// 一个区块内的所有条带
type Quintets struct {
	ObjNum int64
	Quin   []Quintet
	// Offset int64
}

// 将对象入队到确定数据布局的中间队列
func (l *Line) Push(objs Object) {
	l.InstituteLine[objs.From] = append(l.InstituteLine[objs.From], objs)
	l.lotalTransNum++
}

// 确定长度最长的队列序号序列
func (l *Line) Adjust() {
	l.total = 4
	maxLength := -1 // 最长队列的长度
	location := -1  // 长度最长队列的机构ID
	length := [4]int{len(l.InstituteLine[0]), len(l.InstituteLine[1]), len(l.InstituteLine[2]), len(l.InstituteLine[3])}
	if Debug == 1 {
		fmt.Println("剩余：", length)
	}
	for i := 0; i < 4; i++ { // 依次确定第 i 长的队列
		for j := 0; j < 4; j++ { // 找出最大队列
			if maxLength < length[j] {
				maxLength = length[j]
				location = j
			}
		}
		if maxLength == 0 {
			l.instituteNum[i] = -1 // 该机构没有交易
			l.total--              // 合法队列数量减一
			length[location] = -1  // 确定位置后不参与后续队列长度的确定
		} else {
			l.instituteNum[i] = location
			length[location] = -1 // 确定位置后不参与后续队列长度的确定
		}
		maxLength = -1
		location = -1
	}
	if Debug == 1 {
		fmt.Println("排序", l.instituteNum)
	}

}

// 从队列获取一个纠删码条带中的数据块布局信息
// 即哪些对象作为一个纠删码条带的数据块
func (l *Line) Pop() (objs []Object) {

	objs = make([]Object, 3)
	if l.total == 4 || l.total == 3 {
		objs[0] = l.InstituteLine[l.instituteNum[0]][0] //从最长的前三个里面取
		objs[1] = l.InstituteLine[l.instituteNum[1]][0]
		objs[2] = l.InstituteLine[l.instituteNum[2]][0]
		l.InstituteLine[l.instituteNum[0]] = l.InstituteLine[l.instituteNum[0]][1:len(l.InstituteLine[l.instituteNum[0]])] //取完删掉
		l.InstituteLine[l.instituteNum[1]] = l.InstituteLine[l.instituteNum[1]][1:len(l.InstituteLine[l.instituteNum[1]])]
		l.InstituteLine[l.instituteNum[2]] = l.InstituteLine[l.instituteNum[2]][1:len(l.InstituteLine[l.instituteNum[2]])]
	} else if l.total == 2 {
		objs[0] = l.InstituteLine[l.instituteNum[0]][0]
		objs[1] = l.InstituteLine[l.instituteNum[0]][1]
		objs[2] = l.InstituteLine[l.instituteNum[1]][0]
		l.InstituteLine[l.instituteNum[0]] = l.InstituteLine[l.instituteNum[0]][2:len(l.InstituteLine[l.instituteNum[0]])]
		l.InstituteLine[l.instituteNum[1]] = l.InstituteLine[l.instituteNum[1]][1:len(l.InstituteLine[l.instituteNum[1]])]
	} else if l.total == 1 {
		objs[0] = l.InstituteLine[l.instituteNum[0]][0]
		objs[1] = l.InstituteLine[l.instituteNum[0]][1]
		objs[2] = l.InstituteLine[l.instituteNum[0]][2]
		l.InstituteLine[l.instituteNum[0]] = l.InstituteLine[l.instituteNum[0]][3:len(l.InstituteLine[l.instituteNum[0]])]
	}
	return objs
}

func mySqrt(x int8) int8 {
	var i int8
	for i = 0; i <= x; i++ {
		res := i * i
		if res == x {
			return i
		} else if res > x {
			return i - 1
		}
	}
	return -1
}

// 已知数据块的来源，获取计算和存储纠删码校验块的机构
func (q *Quintet) Distribute() {
	if len(q.Objs) == 3 {
		A := q.Objs[0].From
		B := q.Objs[1].From
		C := q.Objs[2].From
		if A != B && A != C { // 三个来自不同机构
			// temp := int8(uint8(q.Objs[0].Oid[0]) % 3) // 随便抽一个来计算
			// if temp == 0 {
			// 	q.Compute = A
			// } else if temp == 1 {
			// 	q.Compute = B
			// } else {
			// 	q.Compute = C
			// }
			q.Storage = append(q.Storage, 6-A-B-C) // 剩下一个来存储
			q.Compute = q.Storage[0]
		}
		if A == B && A != C { // 两个来自同一家机构
			// q.Compute = q.Objs[0].From // 第一家来算
			a := 6 - A - C
			b := 14 - A*A - C*C
			D := ((a + (mySqrt(2*b - a*a))) / 2) % 4
			//fmt.Println(a, b, mySqrt(2*b-a^2))
			//q.Storage = append(q.Storage, (a+(2*b-a^2)^(1/2))/2)//A+B=6-C-D
			//q.Storage = append(q.Storage, (a-(2*b-a^2)^(1/2))/2)//A^2+B^2=14-C^2-D^2
			q.Storage = append(q.Storage, D)
			q.Storage = append(q.Storage, (6 - A - C - D))
			q.Compute = q.Storage[0]
		}
		if A == B && A == C { // 三个来自同一家机构，其他三家各自存其中一个对象
			q.Compute = q.Objs[0].From
			q.Storage = append(q.Storage, (A+1)%4)
			q.Storage = append(q.Storage, (A+2)%4)
			q.Storage = append(q.Storage, (A+3)%4)
		}
	} else {
		if len(q.Objs) == 1 {
			q.Compute = q.Objs[0].From
			q.Storage = append(q.Storage, (q.Objs[0].From+int8(uint8(q.Objs[0].Oid[0])%3+1))%4)
			/*} else {                           直接拆成一个存副本算了 或者挪到下一个区块？
			if q.Objs[0].From == q.Objs[1].From{
				q.Compute = q.Objs[0].From
				q.Storage = append(q.Storage, int8(uint8(q.Objs[0].Oid[0]) % 3))
				q.Storage = append(q.Storage, int8(uint8(q.Objs[1].Oid[0]) % 3))
			}else{
				q.Compute = int8(uint8(q.Objs[0].Oid[0]) % 3)
			}*/
		}
	}

}

func (q *Quintet) Print() {
	fmt.Print("区块：", q.BlockId, "\t")
	fmt.Print("序号：", q.Number, "\t")
	fmt.Print("来源：")
	for _, t := range q.Objs {
		fmt.Print(t.From, " ")
	}
	fmt.Print("\t计算: ", q.Compute)
	fmt.Print("\t存储: ", q.Storage, "\n")
}

// 根据从区块链获取的区块信息确定数据布局
func GenerateDataLayout(b Block) (q Quintets) {
	var line Line
	var dataLayout []Quintet // 数据布局信息
	var tq Quintets          // 区块内的数据布局
	var BlockId uint64       // 区块ID
	fmt.Printf("cluster [%d] -> cal block [%d] data distribute\n", *ClusterID, b.BlockId)
	Objs := b.Objs
	BlockId = b.BlockId
	tq.ObjNum = int64(len(Objs))
	j := 0
	for _, objs := range Objs { // 将所有对象按机构放入到四个队列中
		line.Push(objs)
	}
	for i := 0; i < len(Objs)/3; i++ { // 循环确定数据布局，对象数目大于等于3的部分
		var quintet Quintet // 纠删码条带信息
		quintet.Number = j
		quintet.BlockId = BlockId
		j++
		line.Adjust() // 将最长的三个队列放到最前面
		quintet.Objs = append(quintet.Objs, line.Pop()...)
		quintet.Distribute() // 确定纠删码计算方和存储方
		//quintet.Print()
		dataLayout = append(dataLayout, quintet) //
	}
	line.Adjust()         // 获取最长的三个队列
	if len(Objs)%3 != 0 { // 处理最后小于三个对象
		if line.total == 1 { // 都在一个队列里
			for i := 0; i < len(Objs)%3; i++ { //交易出队
				var quintet Quintet
				quintet.Number = j
				quintet.BlockId = BlockId
				j++
				line.Adjust() // 将最长的三个队列放到最前面
				quintet.Objs = append(quintet.Objs, line.InstituteLine[line.instituteNum[0]][i])
				quintet.Distribute() //确定冗余方式
				//quintet.Print()
				dataLayout = append(dataLayout, quintet)
			}
		} else { // 在两个队列里
			for i := 0; i < len(Objs)%3; i++ { //交易出队
				var quintet Quintet
				quintet.Number = j
				quintet.BlockId = BlockId
				j++
				line.Adjust() // 将最长的三个队列放到最前面
				quintet.Objs = append(quintet.Objs, line.InstituteLine[line.instituteNum[i]][0])
				quintet.Distribute() //确定冗余方式
				//quintet.Print()
				dataLayout = append(dataLayout, quintet)
			}
		}
	}
	tq.Quin = append(tq.Quin, dataLayout...)
	return tq

}
